#include "io.h"

/////////////////////////////////////////////////////////////////////////////
//版本信息
#define VERSION_MAJOR                   	0
#define VERSION_MINOR                   	1

/////////////////////////////////////////////////////////////////////////////
// 1，站号：1-网口一排 和 2 - 扩展板  DO(闭合:0 断开:1) DI(24V接入:1 24V未接:0)
// 2，8路DI对应地址：00 ~ 08
// 3，6路DO对应地址：00 ~ 06
// 通过Mobdus TCP Modbus地址:1 离散寄存器地址: DI:100~115   DOStae 116~ 127  
//                             保持寄存器地址: DOCtrl:100~111
// 通过Redis交互约定：       DI:Bams_DI1 Bams_DI16
//                           DO:Bams_DO1 Bams_DO12

/////////////////////////////////////////////////////////////////////////////
// 更新记录
// 0.1 - 

CIO::CIO(QObject *parent)
	: QThread(parent)
{
	m_pSerial = new CXnSerial;

	//m_pRTDB = 0;
	m_pIOModule = 0;

	m_bSerialOK = false;
#ifdef XNWIN
	m_strPortName = "com4";         // 串口名
#else
	m_strPortName = "/dev/ttyS5";   // 串口名 
#endif 
	m_iBaudRate = 115200;     // 波特率
	m_iDataBits = 8;          // 数据位
	m_iStopBits = 1;          // 停止位
	m_iParity = 0;            // 校验位	
	m_iRtscts = 0;

#if XNWIN
	m_bPrintMsg = true;
#else
	m_bPrintMsg = false;
#endif

	m_iSerialDataLen = 0;
	m_pSerialDataBuf = new char[DATA_BUFFER_LENGHT];  // data buffer

	printf("XnIO Version %d.%d RTDB %d.%d.%d\n", VERSION_MAJOR, VERSION_MINOR, BAMS_VERSION_MAJOR, BAMS_VERSION_MINOR, BAMS_VERSION_REVISION);
}

CIO::~CIO()
{
	if (m_pSerial)
	{
		delete m_pSerial;
		m_pSerial = 0;
	}

	if (m_pSerialDataBuf)
	{
		delete[] m_pSerialDataBuf;
		m_pSerialDataBuf = 0;
	}	
}

int CIO::init()
{
	// some variables
	int iRetVal = -999;

	/*
	// Init RTDB
	m_pRTDB = new CRTDB(1, 1, MAX_BANK_NUM, 10, 10, 10, 10, 10);
	if (!m_pRTDB || m_pRTDB->initRTDB(true) < 0)
	{
		iRetVal = -1;
	}
	else
	{
		m_pIOModule = m_pRTDB->getIOModule();
		if (m_pIOModule)
		{					
			iRetVal = 0;
		}
	}*/
	m_pIOModule = ::getIOModule();
    iRetVal = 0;
	
	return iRetVal;	
}

//CIOModule* CIO::getIOModule()
//{
//	return m_pIOModule;
//}

void CIO::printSendInfo(char* pDataBuf, int iLen)
{
	if (m_bPrintMsg)
	{
		QString strPrintInfo = "";
		QChar aFillChar = '0';

		for (int i = 0; i < iLen; ++i)
		{
			strPrintInfo += QString("%1 ").arg((uchar)*(pDataBuf + i), 2, 16, aFillChar);
		}

		strPrintInfo = strPrintInfo.toUpper();

		strPrintInfo = "Send Datagram:" + strPrintInfo + "\n";

		printf(strPrintInfo.toLatin1().data());
	}	
}

void CIO::printReceInfo(char* pDataBuf, int iLen)
{
	if (m_bPrintMsg)
	{
		QString strPrintInfo = "";
		QChar aFillChar = '0';

		for (int i = 0; i < iLen; ++i)
		{
			strPrintInfo += QString("%1 ").arg((uchar)*(pDataBuf + i), 2, 16, aFillChar);
		}

		strPrintInfo = strPrintInfo.toUpper();

		strPrintInfo = "Rece Datagram:" + strPrintInfo + "\n";

		printf(strPrintInfo.toLatin1().data());
	}
}

// Read DI type = 1   0x02
// Write DO type = 2  0x05
// Read DO type = 3   0x01
int CIO::formDatagram(int iType, int iModbusID, int iDOIndex, quint16 usDOCtrlVal)
{
	int iLen = 0;
	quint16 usTemp = 0;
	if (1 == iModbusID || 2 == iModbusID)
	{
		if (1 == iType)       // Read DI type = 1   0x02
		{
			iLen = 0;
			memset(m_pSerialDataBuf, 0, DATA_BUFFER_LENGHT);

			*(m_pSerialDataBuf + iLen) = (char)iModbusID;
			iLen += 1;

			*(m_pSerialDataBuf + iLen) = 0x02;
			iLen += 1;

			usTemp = 0x0000;
			addParam2Buffer(m_pSerialDataBuf + iLen, usTemp);
			iLen += 2;

			usTemp = 8;
			addParam2Buffer(m_pSerialDataBuf + iLen, usTemp);
			iLen += 2;

			addModbusCRC2Buffer(m_pSerialDataBuf, iLen);
			iLen += 2;
		}
		else if (2 == iType)  // Write DO type = 2  0x05
		{
			iLen = 0;
			memset(m_pSerialDataBuf, 0, DATA_BUFFER_LENGHT);

			*(m_pSerialDataBuf + iLen) = (char)iModbusID;
			iLen += 1;

			*(m_pSerialDataBuf + iLen) = 0x05;
			iLen += 1;

			usTemp = (quint16)(iDOIndex);
			addParam2Buffer(m_pSerialDataBuf + iLen, usTemp);
			iLen += 2;

			if (usDOCtrlVal > 0)
			{
				usTemp = 0xFF00;
			}
			else
			{
				usTemp = 0x0000;
			}
			addParam2Buffer(m_pSerialDataBuf + iLen, usTemp);
			iLen += 2;

			addModbusCRC2Buffer(m_pSerialDataBuf, iLen);
			iLen += 2;
		}
		else if (3 == iType)  // Read DO type = 3   0x01
		{
			iLen = 0;
			memset(m_pSerialDataBuf, 0, DATA_BUFFER_LENGHT);

			*(m_pSerialDataBuf + iLen) = (char)iModbusID;
			iLen += 1;

			*(m_pSerialDataBuf + iLen) = 0x01;
			iLen += 1;

			usTemp = 0x00;
			addParam2Buffer(m_pSerialDataBuf + iLen, usTemp);
			iLen += 2;

			usTemp = 6;
			addParam2Buffer(m_pSerialDataBuf + iLen, usTemp);
			iLen += 2;

			addModbusCRC2Buffer(m_pSerialDataBuf, iLen);
			iLen += 2;
		}
		else
		{
			iLen = -2;
		}
	}
	else
	{
		iLen = -1;
	}

	return iLen;
}

int g_readDiCount = 0;
int CIO::analyDatagram(int iType, int iModbusID, char* pDataBuf, int iLen)
{
	// some varibales
	int iIndex = 0;
	int iIndex1 = 0;
	uchar uaModbusAddr = 0;
	uchar uaFuncCode = 0;
	uchar uaByteNum = 0;
	uchar uaData= 0;
	quint16 usTemp16 = 0;

	if (0 == pDataBuf || iLen < 6)
	{
		return -1;
	}

	// 校验报文是否正确
	iIndex = 0;

	uaModbusAddr = *(uchar*)pDataBuf;
	++iIndex;

	uaFuncCode = *(uchar*)(pDataBuf + iIndex);
	++iIndex;

	// get CRC
	getParamFromBuffer(pDataBuf + iLen - 2, usTemp16);

	if (usTemp16 != Modbus_CRC16(pDataBuf, iLen - 2))
	{
		return -2;
	}

	if (iModbusID != uaModbusAddr)
	{
		return -3;
	}

	if (1 == iType) // Read DI type = 1   0x02
	{
		if (0x02 != uaFuncCode)
		{
			return -4;
		}
	}
	else if (2 == iType) // Write DO type = 2  0x05
	{
		if (0x05 != uaFuncCode)
		{
			return -4;
		}
	}
	else if (3 == iType)  // Read DO type = 3   0x01
	{
		if (0x01 != uaFuncCode)
		{
			return -4;
		}
	}
	else
	{
		return -4;
	}

	// 数据解析
	if (1 == iType) // Read DI type = 1   0x02
	{
		uaByteNum = *(uchar*)(pDataBuf + iIndex);
		++iIndex;
		uaData = *(uchar*)(pDataBuf + iIndex);
		++iIndex;

		for (iIndex1 = 0; iIndex1 < 8; ++iIndex1)
		{
			if ((iModbusID - 1) * 8 + iIndex1 < 16)
			{
				if ((uaData & 0x01) > 0)
				{
					m_pIOModule->usDI[(iModbusID -1) * 8 + iIndex1] = 1;
				}
				else
				{
					m_pIOModule->usDI[(iModbusID - 1) * 8 + iIndex1] = 0;
				}

				uaData = uaData >> 1;
			}
		}
		
		if(g_readDiCount < 30)
		{
			g_readDiCount += 1;
			printf("read DI1-8:%d-%d-%d-%d-%d-%d-%d-%d\n", 
				m_pIOModule->usDI[0],m_pIOModule->usDI[1],m_pIOModule->usDI[2],m_pIOModule->usDI[3],
				m_pIOModule->usDI[4],m_pIOModule->usDI[5],m_pIOModule->usDI[6],m_pIOModule->usDI[7]);
			printf("read DI9-16:%d-%d-%d-%d-%d-%d-%d-%d\n", 
				m_pIOModule->usDI[8],m_pIOModule->usDI[9],m_pIOModule->usDI[10],m_pIOModule->usDI[11],
				m_pIOModule->usDI[12],m_pIOModule->usDI[13],m_pIOModule->usDI[14],m_pIOModule->usDI[15]);
		}
		iIndex = 0;		
	}
	else if (2 == iType) // Write DO type = 2  0x05
	{
		iIndex = 0;
	}
	else if (3 == iType)  // Read DO type = 3   0x01
	{
		uaByteNum = *(uchar*)(pDataBuf + iIndex);
		++iIndex;
		uaData = *(uchar*)(pDataBuf + iIndex);
		++iIndex;

		for (iIndex1 = 0; iIndex1 < 6; ++iIndex1)
		{		
			if ((iModbusID - 1) * 6 + iIndex1 < 12)
			{
				if ((uaData & 0x01) > 0)
				{
					m_pIOModule->usDOState[(iModbusID - 1) * 6 + iIndex1] = 1;
				}
				else
				{
					m_pIOModule->usDOState[(iModbusID - 1) * 6 + iIndex1] = 0;
				}

				uaData = uaData >> 1;
			}	
		}
			
		iIndex = 0;
	}
	else
	{
		return -4;
	}

	return iIndex;		
}

void CIO::readDataFromSerial()
{
	int iTryReadLen = 0;
	int iTryReadNum = 0;

	memset(m_pSerialDataBuf, 0, SNED_BUFFER_LENGHT);
	m_iSerialDataLen = m_pSerial->readBlock((unsigned char *)m_pSerialDataBuf, SNED_BUFFER_LENGHT, 10); // 100ms

	if (m_iSerialDataLen > 0)
	{
		iTryReadLen = m_iSerialDataLen;
		iTryReadNum = 0;

		while (iTryReadLen > 0 && iTryReadNum < 1 && iTryReadLen < SNED_BUFFER_LENGHT)
		{
			iTryReadLen = m_pSerial->readBlock((unsigned char *)(m_pSerialDataBuf + m_iSerialDataLen), SNED_BUFFER_LENGHT - m_iSerialDataLen, 10);

			if (iTryReadLen > 0)
			{
				m_iSerialDataLen += iTryReadLen;

				if (m_iSerialDataLen >= SNED_BUFFER_LENGHT)
				{
					break;
				}
			}

			++iTryReadNum;
		}
	}
}


void CIO::run()
{
	sleep(1);

	// open serial
	int iSpeed = B9600;
	if (2400 == m_iBaudRate)
	{
		iSpeed = B2400;
	}
	else if (4800 == m_iBaudRate)
	{
		iSpeed = B4800;
	}
	else if (9600 == m_iBaudRate)
	{
		iSpeed = B9600;
	}
	else if (19200 == m_iBaudRate)
	{
		iSpeed = B19200;
	}
	else if (115200 == m_iBaudRate)
	{
		iSpeed = B115200;
	}
	else
	{
		iSpeed = B9600;
	}

	if(m_pSerial->openDevice(m_strPortName.toLatin1().data(), iSpeed, 1, m_iRtscts, m_iDataBits, m_iStopBits, m_iParity) >= 0)	
	{
		m_bSerialOK = true;
		printf(QString("Open serial(%1) OK!\n").arg(m_strPortName).toLatin1().data());
	}
	else
	{
		printf(QString("Open serial(%1) FAILED!\n").arg(m_strPortName).toLatin1().data());
	}
	
	// Some Varaiables
	int iIndex = 0;
	int iIndexDO = 0;

	while (m_bSerialOK)
	{
		for (iIndex = 0; iIndex < 2; ++iIndex)
		{
			// Read DI type = 1
			m_iSerialDataLen = formDatagram(1, iIndex + 1);
			if (m_iSerialDataLen >= 8)
			{
				printSendInfo(m_pSerialDataBuf, m_iSerialDataLen);
				if (m_iSerialDataLen == m_pSerial->writeBlock((unsigned char *)m_pSerialDataBuf, m_iSerialDataLen))
				{
					// Read from serial
					readDataFromSerial();
					printReceInfo(m_pSerialDataBuf, m_iSerialDataLen);

					if (m_iSerialDataLen >= 6) // read OK
					{
						analyDatagram(1, iIndex + 1, m_pSerialDataBuf, m_iSerialDataLen);
					}
				}
			}

			// Write DO type = 2
			for (iIndexDO = 0; iIndexDO < 6; ++iIndexDO)
			{
				if (iIndex * 6 + iIndexDO < 12)
				{
					if (m_pIOModule->usDOState[iIndex * 6 + iIndexDO] != m_pIOModule->usDOCtrl[iIndex * 6 + iIndexDO])
					{
						m_iSerialDataLen = formDatagram(2, iIndex + 1, iIndexDO, m_pIOModule->usDOCtrl[iIndex * 6 + iIndexDO]);
						printSendInfo(m_pSerialDataBuf, m_iSerialDataLen);
						if (m_iSerialDataLen >= 8)
						{
							if (m_iSerialDataLen == m_pSerial->writeBlock((unsigned char *)m_pSerialDataBuf, m_iSerialDataLen))
							{
								// Read from serial
								readDataFromSerial();
								printReceInfo(m_pSerialDataBuf, m_iSerialDataLen);

								if (m_iSerialDataLen >= 6) // read OK
								{
									analyDatagram(2, iIndex + 1, m_pSerialDataBuf, m_iSerialDataLen);
								}
							}
						}
					}
				}				
			}

			// Read DO type = 3
			m_iSerialDataLen = formDatagram(3, iIndex + 1);
			printSendInfo(m_pSerialDataBuf, m_iSerialDataLen);
			if (m_iSerialDataLen >= 8)
			{
				if (m_iSerialDataLen == m_pSerial->writeBlock((unsigned char *)m_pSerialDataBuf, m_iSerialDataLen))
				{
					// Read from serial
					readDataFromSerial();
					printReceInfo(m_pSerialDataBuf, m_iSerialDataLen);

					if (m_iSerialDataLen >= 6) // read OK
					{
						analyDatagram(3, iIndex + 1, m_pSerialDataBuf, m_iSerialDataLen);
					}
				}
			}
		}		
	}		
}

int CIO::prepareExcepDatagram(uchar uaExcepCode)
{
	int iIndex = 0;

	// Func Code
	iIndex = 1;
	*(m_pSerialDataBuf + iIndex) |= 0x80;
	iIndex += 1;

	// Err Code
	*(m_pSerialDataBuf + iIndex) = uaExcepCode;
	iIndex += 1;

	// CRC
	addModbusCRC2Buffer(m_pSerialDataBuf, iIndex);
	iIndex += 2;

	return iIndex;
}


int CIO::getParamFromBuffer(char* pData, quint16& uiParamVal)
{
	uiParamVal = (unsigned char)*pData;
	uiParamVal = uiParamVal * 256;

	++pData;

	uiParamVal += (unsigned char)*pData;

	return 0;
}

int CIO::addParam2Buffer(char* pData, quint16 uiParamVal)
{
	*pData = (char)(uiParamVal >> 8);

	++pData;
	*pData = (char)(uiParamVal);

	return  0;
}

// RTU
int CIO::addModbusCRC2Buffer(char *pData, int iLen)
{
	quint16 usCRC = Modbus_CRC16(pData, iLen);

	return addParam2Buffer(pData + iLen, usCRC);
}

//********************************************************************
// FUCTION: Modbus_CRC16 —— MODBUS RTU CRC16校验生成函数
// VERSION: 1	(2010-9-27 14:34:01)
// INPUT:	dat_buf 待校验数值缓存区
// 			dat_len 待校验数值缓存区的长度
// OUTPUT:	CRC16校验值
// EXAMPLE:
// 输入数据 dat_buf指向的数组为： 0x33, 0x78, 0x98, 0x60, 校验长度为4
// 则输出为：高字节0xE4 低字节0x91
//********************************************************************
quint16 CIO::Modbus_CRC16(char *dat_buf, quint16 usLen)  
{
	/* CRC 高位字节值表 */
	const uchar AUCHCRCHI[256]=
	{
		0x00,0x0C1,0x81,0x40,0x01,0x0C0,0x80,0x41,
		0x01,0x0C0,0x80,0x41,0x00,0x0C1,0x81,0x40,
		0x01,0x0C0,0x80,0x41,0x00,0x0C1,0x81,0x40,
		0x00,0x0C1,0x81,0x40,0x01,0x0C0,0x80,0x41,
		0x01,0x0C0,0x80,0x41,0x00,0x0C1,0x81,0x40,
		0x00,0x0C1,0x81,0x40,0x01,0x0C0,0x80,0x41,
		0x00,0x0C1,0x81,0x40,0x01,0x0C0,0x80,0x41,
		0x01,0x0C0,0x80,0x41,0x00,0x0C1,0x81,0x40,
		0x01,0x0C0,0x80,0x41,0x00,0x0C1,0x81,0x40,
		0x00,0x0C1,0x81,0x40,0x01,0x0C0,0x80,0x41,
		0x00,0x0C1,0x81,0x40,0x01,0x0C0,0x80,0x41,
		0x01,0x0C0,0x80,0x41,0x00,0x0C1,0x81,0x40,
		0x00,0x0C1,0x81,0x40,0x01,0x0C0,0x80,0x41,
		0x01,0x0C0,0x80,0x41,0x00,0x0C1,0x81,0x40,
		0x01,0x0C0,0x80,0x41,0x00,0x0C1,0x81,0x40,
		0x00,0x0C1,0x81,0x40,0x01,0x0C0,0x80,0x41,
		0x01,0x0C0,0x80,0x41,0x00,0x0C1,0x81,0x40,
		0x00,0x0C1,0x81,0x40,0x01,0x0C0,0x80,0x41,
		0x00,0x0C1,0x81,0x40,0x01,0x0C0,0x80,0x41,
		0x01,0x0C0,0x80,0x41,0x00,0x0C1,0x81,0x40,
		0x00,0x0C1,0x81,0x40,0x01,0x0C0,0x80,0x41,
		0x01,0x0C0,0x80,0x41,0x00,0x0C1,0x81,0x40,
		0x01,0x0C0,0x80,0x41,0x00,0x0C1,0x81,0x40,
		0x00,0x0C1,0x81,0x40,0x01,0x0C0,0x80,0x41,
		0x00,0x0C1,0x81,0x40,0x01,0x0C0,0x80,0x41,
		0x01,0x0C0,0x80,0x41,0x00,0x0C1,0x81,0x40,
		0x01,0x0C0,0x80,0x41,0x00,0x0C1,0x81,0x40,
		0x00,0x0C1,0x81,0x40,0x01,0x0C0,0x80,0x41,
		0x01,0x0C0,0x80,0x41,0x00,0x0C1,0x81,0x40,
		0x00,0x0C1,0x81,0x40,0x01,0x0C0,0x80,0x41,
		0x00,0x0C1,0x81,0x40,0x01,0x0C0,0x80,0x41,
		0x01,0x0C0,0x80,0x41,0x00,0x0C1,0x81,0x40
	};
	
	/* CRC 低位字节值表 */
	const uchar AUCHCRCLO[256]=
	{
		0,0xC0,0xC1,0x01,0xC3,0x03,0x02,0xC2,
		0xC6,0x06,0x07,0xC7,0x05,0xC5,0xC4,0x04,
		0xCC,0x0C,0x0D,0xCD,0x0F,0xCF,0xCE,0x0E,
		0x0A,0xCA,0xCB,0x0B,0xC9,0x09,0x08,0xC8,
		0xD8,0x18,0x19,0xD9,0x1B,0xDB,0xDA,0x1A,
		0x1E,0xDE,0xDF,0x1F,0xDD,0x1D,0x1C,0xDC,
		0x14,0xD4,0xD5,0x15,0xD7,0x17,0x16,0xD6,
		0xD2,0x12,0x13,0xD3,0x11,0xD1,0xD0,0x10,
		0xF0,0x30,0x31,0xF1,0x33,0xF3,0xF2,0x32,
		0x36,0xF6,0xF7,0x37,0xF5,0x35,0x34,0xF4,
		0x3C,0xFC,0xFD,0x3D,0xFF,0x3F,0x3E,0xFE,
		0xFA,0x3A,0x3B,0xFB,0x39,0xF9,0xF8,0x38,
		0x28,0xE8,0xE9,0x29,0xEB,0x2B,0x2A,0xEA,
		0xEE,0x2E,0x2F,0xEF,0x2D,0xED,0xEC,0x2C,
		0xE4,0x24,0x25,0xE5,0x27,0xE7,0xE6,0x26,
		0x22,0xE2,0xE3,0x23,0xE1,0x21,0x20,0xE0,
		0xA0,0x60,0x61,0xA1,0x63,0xA3,0xA2,0x62,
		0x66,0xA6,0xA7,0x67,0xA5,0x65,0x64,0xA4,
		0x6C,0xAC,0xAD,0x6D,0xAF,0x6F,0x6E,0xAE,
		0xAA,0x6A,0x6B,0xAB,0x69,0xA9,0xA8,0x68,
		0x78,0xB8,0xB9,0x79,0xBB,0x7B,0x7A,0xBA,
		0xBE,0x7E,0x7F,0xBF,0x7D,0xBD,0xBC,0x7C,
		0xB4,0x74,0x75,0xB5,0x77,0xB7,0xB6,0x76,
		0x72,0xB2,0xB3,0x73,0xB1,0x71,0x70,0xB0,
		0x50,0x90,0x91,0x51,0x93,0x53,0x52,0x92,
		0x96,0x56,0x57,0x97,0x55,0x95,0x94,0x54,
		0x9c,0x5c,0x5d,0x9d,0x5f,0x9f,0x9e,0x5e,
		0x5a,0x9a,0x9b,0x5b,0x99,0x59,0x58,0x98,
		0x88,0x48,0x49,0x89,0x4b,0x8b,0x8a,0x4a,
		0x4e,0x8e,0x8f,0x4f,0x8d,0x4d,0x4c,0x8c,
		0x44,0x84,0x85,0x45,0x87,0x47,0x46,0x86,
		0x82,0x42,0x43,0x83,0x41,0x81,0x80,0x40 
	};
	
	uchar j,CH,CL;
	quint16 i;
	CH = 0XFF;
	CL = 0XFF;
	for(i = 0; i < usLen; i++)
	{
		j = dat_buf[i];
		j = CH ^ j;
		CH = CL ^ AUCHCRCHI[j];
		CL = AUCHCRCLO[j];
	}
	
	return(CH * 0x100 + CL);
}	

